/*
 * The MIT License
 *
 * Copyright (c) 2010, Marc Schwerdtfeger (marc@schwerdtfeger.de)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.loosix.core.module.impl
{
    import flash.utils.Dictionary;

    import org.loosix.core.Loosix;
    import org.loosix.core.lxc_internal;
    import org.loosix.core.module.Module;
    import org.loosix.core.module.ModuleManager;
    import org.loosix.core.module.constant.ModuleState;
    import org.loosix.core.module.errors.ModuleError;
    import org.loosix.core.module.events.ModuleEvent;

    use namespace lxc_internal;

    /**
     * We don�t use the State-Pattern, because we want only one class
     * for each module.
     */
    public class AbstractModule implements Module
    {

        protected var _interfaceName:String;
        protected var _moduleName:String;

        protected var _dependencies:Array;
        protected var _description:String;

        protected var _state:String;

        protected var _autoReplace:Boolean;
        protected var _autoStart:Boolean;

        /**
         *  Constructor of the AbstractModule
         * 
         * @param interfaceName required
         * @param moduleName required
         * @param dependencies optional, default null
         * @param autoStart optional, default true
         * @param autoReplace optional, default false
         * @param description optional, default ""
         */
        public function AbstractModule(interfaceName:String, moduleName:String, dependencies:Array = null,autoStart:Boolean = true, autoReplace:Boolean = false,description:String = "")
        {
            _interfaceName = interfaceName;
            _moduleName = moduleName;

            _dependencies = (dependencies == null) ? []:dependencies;
            _description = description;
            
            _autoStart = autoStart;
            _autoReplace = autoReplace;

            _state = ModuleState.UNREGISTERED;
        } // endConstructor

		
		/**
             if the module is already running, an exception is thrown!

             we could allow to call the method start() while the module is
             already running like:

             if(_state == ModuleState.STARTING || _state == ModuleState.STARTED) return;
           
        */
        public function start(context:Object = null):void
        {            
            setState(ModuleState.STARTING);
            // init services ...
            // when finished call onStartComplete()
            onStartComplete();
        } // endFunc

        protected function onStartComplete():void
        {
            setState(ModuleState.STARTED);
        } // endFunc
                
        public function stop():void
        {
            setState(ModuleState.STOPPING);
            onStopComplete();
        } // endFunc

        protected function onStopComplete():void
        {
            var resolved:Boolean = Loosix.moduleManager.isModuleResolved(this);
            var nextState:String = (resolved) ? ModuleState.RESOLVED : ModuleState.REGISTERED;
            setState(nextState);
        } // endFunc                              

        /* Loosix ModuleManger Handlers */

        public function onPreRegister():void
        {

        } // endFunc

        public function onRegister():void
        {
            setState(ModuleState.REGISTERED);
        } // endFunc

        public function onResolved():void
        {
            setState(ModuleState.RESOLVED);
        } // endFunc

        public function onUnresolved():void
        {
            var event:ModuleEvent = new ModuleEvent(ModuleEvent.MODULE_UNRESOLVED,this);
            Loosix.topicDispatcher.dispatchEvent(ModuleEvent.TOPIC,event);

            // if the dependencies are resolved again, lets restart the module automatically
            autoStart = true;
            Loosix.loggerService.info("Module: '"+_moduleName+"' is unresolved. 'autostart' is set TRUE " +
                                      "to restart the module automatically.",this)

            if(state == ModuleState.STARTING ||
               state == ModuleState.STARTED)
            {
                stop();
            }
            else
            {
               setState(ModuleState.REGISTERED);
            } // endIF
        } // endFunc

        public function onUnRegister():void
        {
           autoStart = false; // to prevent restart 
            
           if(state == ModuleState.STARTED ||
              state == ModuleState.STARTING )
           {
                stop();
           } // endIf

           setState(ModuleState.UNREGISTERED);
        } // endFunc

        /* Getter & Setter */

        public function get interfaceName():String
        {
            return _interfaceName;
        } // endFunc

        public function set interfaceName(value:String):void
        {
            _interfaceName = value;
        } // endFunc

        public function get moduleName():String
        {
            return _moduleName;
        } // endFunc

        public function set moduleName(value:String):void
        {
            _moduleName = value;
        } // endFunc

        public function get dependencies():Array
        {
            if(_dependencies == null) _dependencies = [];
            return _dependencies;
        } // endFunc

        public function set dependencies(value:Array):void
        {
            _dependencies = value;
        } // endFunc

        public function get state():String
        {
            return _state;
        } // endFunc
        
        public function get description():String
        {
            if(_description == null) return "";
            else return _description;
        } // endFunc

        public function set description(value:String):void
        {
            _description = value;
        } // endFunc

        public function get autoStart():Boolean
        {
            return _autoStart;
        } // endFunc

        public function set autoStart(value:Boolean):void
        {
            _autoStart = value;
        } // endFunc

        public function get autoReplace():Boolean
        {
            return _autoReplace;
        } // endFunc

        public function set autoReplace(value:Boolean):void
        {
            _autoReplace = value;
        } // endFunc

        /**
         * Protected or Private setters are not allowed! So we use setState internally
         * @param newState the new State
         */
        protected final function setState(newState:String):void
        {
            if(!ModuleState.isValidStateChange(_state,newState))
            {
                throw new ModuleError(ModuleError.getErrorMessage(ModuleError.INVALID_STATE_CHANGE,this,[_state,newState]),
                                     ModuleError.INVALID_STATE_CHANGE);
            } // endIf

            // Map the state to the right event.
            var stateEventMap:Dictionary = new Dictionary();

            stateEventMap[ModuleState.REGISTERED] = ModuleEvent.MODULE_REGISTERED;
            stateEventMap[ModuleState.RESOLVED] = ModuleEvent.MODULE_RESOLVED;
            stateEventMap[ModuleState.STARTING] = ModuleEvent.MODULE_STARTING;
            stateEventMap[ModuleState.STARTED] = ModuleEvent.MODULE_STARTED;
            stateEventMap[ModuleState.STOPPING] = ModuleEvent.MODULE_STOPPING;
            stateEventMap[ModuleState.UNREGISTERED] = ModuleEvent.MODULE_UNREGISTERED;
                        
            _state = newState;

            var event:ModuleEvent = new ModuleEvent(stateEventMap[newState],this);
            Loosix.topicDispatcher.dispatchEvent(ModuleManager.TOPIC,event);
        } // endFunc

        public function toString():String
        {
            return "org.loosix.core.module.impl.AbstractModule{name:"+_moduleName+" state=" + String(_state) + "}";
        } // endFunc
        
    } // endClass

} // endPackage